/*
 * AIEngine a new generation network intrusion detection system.
 *
 * Copyright (C) 2013-2023  Luis Campo Giralte
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 * Written by Luis Campo Giralte <luis.camp0.2009@gmail.com>
 *
 * This file contains an example of how to read alarms from the
 * alerted queue and print on the screen or send to a web service
 * via HTTP by using the /v1/alerts URI
 *
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/program_options.hpp>
#include <cstdlib>
#include <string>
#include <iostream>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include "json.hpp"

namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
using tcp = net::ip::tcp;
using namespace std;

// structure for message queue
struct mesg_buffer {
	long mesg_type;
    	char name[256];
	char mesg_text[1024];
} message;

void alarm_handler(int s) {

	std::cout << "Timeout exceeed" << std::endl;
	exit(0);
}


int main(int argc, char* argv[]) {

	std::string host;
	int port, nalarms;
	std::string uri;
	int timeout = 0;

    	key_t key = ftok("/etc/", 65);
  
    	int msgid = msgget(key, 0666 | IPC_CREAT);

        namespace po = boost::program_options;
        po::variables_map var_map;

        po::options_description options("Optional arguments");
        options.add_options()
                ("host,H",   po::value<std::string>(&host),
                        "Sets the hostname for send the alarm.")
                ;

        options.add_options()
                ("port,p",    po::value<int>(&port)->default_value(80),
                        "Sets the port number.")
                ;

        options.add_options()
                ("alarms,n",    po::value<int>(&nalarms)->default_value(1),
                        "Sets the number of messages queues to be read.")
                ;

        options.add_options()
                ("uri,u",   po::value<std::string>(&uri),
                        "Sets the access URI.")
                ;
        options.add_options()
                ("timeout,t",   po::value<int>(&timeout)->default_value(0),
                        "Sets the timeout for wait on the queue.")
                ;

        try {
                po::store(po::parse_command_line(argc, argv, options), var_map);

                if (var_map.count("help")) {
                        std::cout << options << std::endl;
                        std::cout << std::endl;
                        exit(0);
                }
                po::notify(var_map);

        } catch(boost::program_options::required_option& e) {
                std::cerr << "Error: " << e.what() << std::endl;
                std::cout << options << std::endl;
                exit(-1);
        } catch(std::exception& e) {
                std::cerr << "Unsupported option." << e.what() << std::endl;
                std::cout << options << std::endl;
                exit(-2);
        }
	try {
		net::io_context ioc;
		tcp::resolver resolver(ioc);

#if BOOST_VERSION >= 107000
		beast::tcp_stream stream(ioc);
#else // legacy
		tcp::socket stream{ioc};
#endif
		signal(SIGALRM, alarm_handler);

		for (int i = 0; i < nalarms; ++i) {

			if (timeout > 0)
				alarm(timeout);

			msgrcv(msgid, &message, sizeof(message), 1, 0);

			nlohmann::json j = nlohmann::json::parse(std::string(message.mesg_text));;

			if (host.length() > 0) {
				beast::error_code ec;
				beast::flat_buffer buffer;
				http::response<http::dynamic_body> res;

				// Look up the domain name
				auto const results = resolver.resolve(host, std::to_string(port));

#if BOOST_VERSION >= 107000
				stream.connect(results);
#else // legacy
				boost::asio::connect(stream, results.begin(), results.end());
#endif
				// Set up an HTTP POST request message
				http::request<http::string_body> req{http::verb::post, uri, 11};
				req.set(http::field::host, host);
				req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
				req.set(http::field::content_type, "application/json");
				req.body() = j.dump();
				req.content_length(req.payload_size());

				http::write(stream, req);

				http::read(stream, buffer, res);

				if (res.result_int() == 200)
					std::cout << "Message deliver correctly" << std::endl;

#if BOOST_VERSION >= 107000
				stream.socket().shutdown(tcp::socket::shutdown_both, ec);
#else //legacy
				stream.shutdown(tcp::socket::shutdown_both, ec);
#endif
				if (ec && ec != beast::errc::not_connected)
					throw beast::system_error{ec};
			} else {
				std::cout << "From:" << message.name << std::endl;
				std::cout << j.dump(4) << std::endl;
			}
		}
	} catch(std::exception const& e) {
		std::cerr << "Error: " << e.what() << std::endl;
		return EXIT_FAILURE;
	}
  
	return 0;
}
